/*
 *    Copyright 2022 Huawei Technologies Co., Ltd.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.edgegallery.appstore.infrastructure.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.parallel.InputStreamSupplier;
import org.apache.commons.io.input.NullInputStream;
import org.edgegallery.appstore.domain.constants.ResponseConst;
import org.edgegallery.appstore.domain.shared.exceptions.AppException;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component("ScatterSampleUtil")
public class ScatterCompressUtil {

    public static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(AppUtil.class);

    private static final String ZIP_PACKAGE_ERR_MESSAGES = "failed to zip application package";

    /**
     * compress and delete file.
     * @param destinationFile destinationFile.
     * @param fileZipName fileZipName.
     * @throws Exception Exception.
     */
    public void compressAndDeleteFile(String destinationFile, String fileZipName) throws Exception {
        LOGGER.info("Begin to compress file.");
        final File result = new File(fileZipName);
        createZipFile(destinationFile, result);
        try {
            org.apache.commons.io.FileUtils.deleteDirectory(new File(destinationFile));
        } catch (IOException e) {
            throw new AppException("failed to delete directory", ResponseConst.RET_DEL_DIR_FAILED);
        }
        LOGGER.info("Add image to compressed file successfully.");
    }

    static class CustomInputStreamSupplier implements InputStreamSupplier {
        private File currentFile;

        public CustomInputStreamSupplier(File currentFile) {
            this.currentFile = currentFile;
        }

        @Override
        public InputStream get() {
            try {
                return currentFile.isDirectory() ? new NullInputStream(0) : new FileInputStream(currentFile);
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

    /**
     * add file into folder.
     * @param entryName entryName.
     * @param currentFile currentFile.
     * @param scatterCompress scatterCompress.
     * @throws IOException IOException.
     */
    private void addEntry(String entryName, File currentFile, ScatterCompress scatterCompress) throws IOException {
        ZipArchiveEntry archiveEntry = new ZipArchiveEntry(entryName);
        archiveEntry.setMethod(ZipEntry.STORED);
        final InputStreamSupplier supp = new CustomInputStreamSupplier(currentFile);
        scatterCompress.addEntry(archiveEntry, supp);
    }

    /**
     * compress current directory.
     * @param dir file dir.
     * @param scatterCompress scatterCompress object.
     * @throws IOException IOException.
     */
    private void compressCurrentDirectory(File dir, ScatterCompress scatterCompress) throws IOException {
        if (dir == null) {
            throw new AppException(ZIP_PACKAGE_ERR_MESSAGES, ResponseConst.RET_COMPRESS_FAILED);
        }
        String relativePath = "";
        if (dir.isFile()) {
            relativePath = dir.getName();
            addEntry(relativePath, dir, scatterCompress);
            return;
        }
        if (dir.exists() && dir.isDirectory()) {
            File[] files = dir.listFiles();
            if (files != null && files.length > 0) {
                for (File file : files) {
                    if (file.isDirectory()) {
                        compressCurrentDirectory(file, scatterCompress);
                    } else {
                        relativePath = file.getParent()
                                .replace(scatterCompress.getRootPath() + File.separator, "");
                        if (relativePath.equalsIgnoreCase(dir.getAbsolutePath())) {
                            relativePath = file.getName();
                        } else {
                            relativePath = relativePath + File.separator + file.getName();
                        }
                        addEntry(relativePath, file, scatterCompress);
                    }
                }
            } else {
                relativePath = dir.getAbsolutePath().replace(scatterCompress.getRootPath(), "");
                addEntry(relativePath + File.separator, dir, scatterCompress);
                return;
            }
        }
    }

    /**
     * create zip file.
     * @param rootPath rootPath.
     * @param result result.
     * @throws Exception exception.
     */
    private void createZipFile(String rootPath, File result) throws Exception {
        File dstFolder = new File(result.getParent());
        boolean success = false;
        if (!dstFolder.isDirectory()) {
            success = dstFolder.mkdirs();
        }
        if (!success) {
            LOGGER.info("parent directory existed.");
        }
        File rootDir = new File(rootPath);
        final ScatterCompress scatterCompress = new ScatterCompress(rootDir.getAbsolutePath());
        compressCurrentDirectory(rootDir, scatterCompress);
        final ZipArchiveOutputStream zipArchiveOutputStream = new ZipArchiveOutputStream(result);
        scatterCompress.writeTo(zipArchiveOutputStream);
        zipArchiveOutputStream.close();
    }
}
